package com.openx.cloud.server.autoconfigure.swagger;

import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.SwaggerDefinition;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeansException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Swagger文档生成配置
 *
 * @author liuyadu
 */
@Slf4j
@Configuration
@ConditionalOnWebApplication
@EnableConfigurationProperties(SwaggerProperties.class)
public class SwaggerAutoConfiguration implements ApplicationContextAware {

    private ApplicationContext applicationContext;


    /**
     * 开启swagger
     *
     * @return
     */
    @ConditionalOnMissingBean(Swagger2DocumentationConfiguration.class)
    @ConditionalOnProperty(value = "swagger.enable", havingValue = "true", matchIfMissing = true)
    public Swagger2DocumentationConfiguration swagger2DocumentationConfiguration() {
        return new Swagger2DocumentationConfiguration();
    }

    /**
     * 接口注册扫描
     *
     * @param amqpTemplate
     * @param swaggerProperties
     * @return
     */
    @ConditionalOnProperty(value = "swagger.register", matchIfMissing = false)
    @Bean
    public RequestMappingScan requestMappingScan(AmqpTemplate amqpTemplate, SwaggerProperties swaggerProperties) {
        return new RequestMappingScan(amqpTemplate, swaggerProperties, getApiInfo(applicationContext));
    }

    /**
     * 获取接口文档信息
     *
     * @param applicationContext
     * @return
     */
    ApiInfo getApiInfo(ApplicationContext applicationContext) {
        Map<String, Object> map = applicationContext.getBeansWithAnnotation(SwaggerDefinition.class);
        SwaggerDefinition annotation = null;
        if (map != null && !map.isEmpty()) {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                Class<?> clazz = entry.getValue().getClass();
                if (clazz.isAnnotationPresent(SwaggerDefinition.class)) {
                    // 查询 "类" 上的注解
                    annotation = clazz.getAnnotation(SwaggerDefinition.class);
                }
            }
        }
        Environment environment = applicationContext.getEnvironment();
        String env = environment.getProperty("spring.profiles.active");
        List<VendorExtension> extensions = new ArrayList<>();
        // 当前环境
        extensions.add(new StringVendorExtension("env", env));
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title(annotation != null ? annotation.info().title() : "")
                .description(annotation != null ? annotation.info().description() : "")
                .termsOfServiceUrl(annotation != null ? annotation.info().termsOfService() : "")
                .version(annotation != null ? annotation.info().version() : "")
                .extensions(extensions)
                .build();
        return apiInfo;
    }

    /**
     * swagger配置类
     */
    @ConditionalOnBean(Swagger2DocumentationConfiguration.class)
    @Configuration
    class SwaggerConfiguration {

        @Bean
        public Docket createRestApi() {
            ApiInfo apiInfo = getApiInfo(applicationContext);
            log.info("开启Swagger文档:{}", JSONObject.toJSONString(apiInfo));

            return new Docket(DocumentationType.SWAGGER_2)
                    .apiInfo(apiInfo)
                    .select()
                    .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
                    .paths(PathSelectors.any())
                    .build()
                    // 添加全局参数
                    .globalOperationParameters(parameters())
                    .securitySchemes(Collections.singletonList(securityScheme()));
        }

        /**
         * 构建全局参数
         *
         * @return
         */
        private List<Parameter> parameters() {
            ParameterBuilder builder = new ParameterBuilder();
            List<Parameter> pars = new ArrayList<Parameter>();
            builder = new ParameterBuilder();
            pars.add(builder.build());
            return pars;
        }


        @Bean
        UiConfiguration uiConfig() {
            return new UiConfiguration(null, "list", "alpha", "schema",
                    UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS, false, true, 60000L);
        }


        /***
         * oauth2配置
         * 需要增加swagger授权回调地址
         * http://localhost:8888/webjars/springfox-swagger-ui/o2c.html
         * @return
         */
        @Bean
        SecurityScheme securityScheme() {
            return new ApiKey("BearerToken", "Authorization", "header");
        }

    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
